﻿namespace GreenIsomer.Web.Controls.JQuery {
	using System;
	using System.ComponentModel;
	using System.Globalization;

	/// <summary>
	/// Defines a grid for snapping.
	/// </summary>
	[TypeConverter(typeof(GridConverter))]
	public struct Grid : IEquatable<Grid>, IComplexProperty {


		#region Static Readonly Fields: Standard values

		/// <summary>
		/// Instructs jQuery UI not to use a grid.
		/// </summary>
		public static readonly Grid None = new Grid();

		#endregion


		#region Members

		private readonly int x;
		private readonly int y;

		#endregion


		#region Constructors

		/// <summary>
		/// Initializes a new GreenIsomer.Web.Controls.JQuery.Grid value identical values for x and y.
		/// </summary>
		/// <param name="value">The value to use for x and y.</param>
		/// <exception cref="ArgumentOutOfRangeException">Value must positive.</exception>
		public Grid(int value) {
			if(value <= 0) throw new ArgumentOutOfRangeException("value", "Value must be positive.");

			x = y = value;
		}

		/// <summary>
		/// Initializes a new GreenIsomer.Web.Controls.JQuery.Grid value using separately specified x and y values.
		/// </summary>
		/// <param name="x">The horizontal size of the grid.</param>
		/// <param name="y">The vertical size of the grid.</param>
		/// <exception cref="ArgumentOutOfRangeException">Both values must positive.</exception>
		public Grid(int x, int y) {
			if(x <= 0) throw new ArgumentOutOfRangeException("x", "Value must be positive.");
			if(y <= 0) throw new ArgumentOutOfRangeException("y", "Value must be positive.");

			this.x = x;
			this.y = y;
		}

		#endregion


		#region Properties

		/// <summary>
		/// The horizontal value of the grid.
		/// </summary>
		public int X {
			get { return x; }
		}

		/// <summary>
		/// The vertical value of the grid.
		/// </summary>
		public int Y {
			get { return y; }
		}

		/// <summary>
		/// Whether or not the x and y values are identical.
		/// </summary>
		public bool IsSquare {
			get { return IsSet && x == y; }
		}

		/// <summary>
		/// Whether or not a grid has been defined.
		/// </summary>
		public bool IsSet {
			get { return x != 0 && y != 0; }
		}

		#endregion


		#region Methods: Object management

		/// <summary>
		/// Determines whether or not the specified object is equal to the current GreenIsomer.Web.Controls.JQuery.Grid value.
		/// </summary>
		/// <param name="obj">The System.Object to compare.</param>
		/// <returns>Whether or not the provided object is equal.</returns>
		public override bool Equals(object obj) {
			if(obj is Grid) return this == (Grid)obj;
			return false;
		}

		/// <summary>
		/// Determines whether or not the specified Grid is equal to the current GreenIsomer.Web.Controls.JQuery.Grid value.
		/// </summary>
		/// <param name="other">The GreenIsomer.Web.Controls.JQuery.Grid to compare.</param>
		/// <returns>Whether or not the provided Grid value is equal.</returns>
		public bool Equals(Grid other) {
			return this == other;
		}

		/// <summary>
		/// Serves as a hash function for GreenIsomer.Web.Controls.JQuery.Grid.
		/// </summary>
		/// <returns>The hash value associated with the contained data.</returns>
		public override int GetHashCode() {
			//TODO: Have TheMuuj check it out
			return x.GetHashCode() & ~y.GetHashCode();
		}

		/// <summary>
		/// Returns a System.String that represents the current GreenIsomer.Web.Controls.JQuery.Grid value.
		/// </summary>
		/// <returns>The string representation.</returns>
		public override string ToString() {
			if(!IsSet) return string.Empty;
			if(IsSquare) return x.ToString(CultureInfo.InvariantCulture);
			return string.Format(CultureInfo.InvariantCulture, "{0},{1}", x, y);
		}

		#endregion


		#region Operators

		/// <summary>
		/// Compares the equality of two GreenIsomer.Web.Controls.JQuery.Grid values.
		/// </summary>
		/// <param name="value0">The first Grid value.</param>
		/// <param name="value1">The second Grid value.</param>
		/// <returns>Whether or not the two Grid values are equal.</returns>
		public static bool operator ==(Grid value0, Grid value1) {
			return value0.x == value1.x && value0.y == value1.y;
		}

		/// <summary>
		/// Compares the inequality of two GreenIsomer.Web.Controls.JQuery.Duration values.
		/// </summary>
		/// <param name="value0">The first Grid value.</param>
		/// <param name="value1">The second Grid value.</param>
		/// <returns>Whether or not the two Grid values are distinct.</returns>
		public static bool operator !=(Grid value0, Grid value1) {
			return value0.x != value1.x || value0.y != value1.y;
		}

		#endregion


		#region Explicit implementation: IPropertyValue

		void IComplexProperty.AddPropertyValue(PropertyCollection properties, string name) {
			if(!IsSet) properties.AddValue(name, false);
			else if(IsSquare) properties.AddValue(name, x);
			else properties.AddValue(name, new int[] { x, y });
		}

		#endregion


	}

}